/*
 * =====================================================================================
 *
 *       Filename:  test_netlink.c
 *
 *    Description:  应用程序测试test_netlink.ko内核模块
 *
 *        Version:  1.0
 *        Created:  2019年12月24日 15时46分35秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  YOUR NAME (), 
 *   Organization:  
 *
 * =====================================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/genetlink.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/mngt.h>
#include "../common.h"
#define ARRAY_SIZE(x) ( sizeof(x) / sizeof((x)[0]))


/* 从内核接收消息才用到 */
static struct nla_policy test_policy[] = {
    [TEST_ATTR_IP]   = { .type = NLA_U32 },
    [TEST_ATTR_PORT] = { .type = NLA_U16},
};

static int test_msg_add_parser(struct nl_cache_ops *unused, struct genl_cmd *cmd,
        struct genl_info *info, void *arg)
{
    uint32_t ip = 0;
    uint16_t port = 0;
    if(info->attrs[TEST_ATTR_IP] && info->attrs[TEST_ATTR_PORT]) {
        ip = nla_get_u32(info->attrs[TEST_ATTR_IP]);
        port = nla_get_u16(info->attrs[TEST_ATTR_PORT]);
        printf("%s(): %u:%u added\n", __func__, ip, ntohs(port));
    }
    else {
        printf("%s() no attr TEST_ATTR_IP or TEST_ATTR_PORT\n", __func__);
    }
    return 0;
}

static int test_msg_del_parser(struct nl_cache_ops *unused, struct genl_cmd *cmd,
        struct genl_info *info, void *arg)
{
    uint32_t ip = 0;
    uint16_t port = 0;
    if(info->attrs[TEST_ATTR_IP] && info->attrs[TEST_ATTR_PORT]) {
        ip = nla_get_u32(info->attrs[TEST_ATTR_IP]);
        port = nla_get_u16(info->attrs[TEST_ATTR_PORT]);
        printf("%s(): %u:%u added\n", __func__, ip, ntohs(port));
    }
    else {
        printf("%s() no attr TEST_ATTR_IP or TEST_ATTR_PORT\n", __func__);
    }
    return 0;
}

static struct genl_cmd test_genl_cmds[] = {
    {
        .c_id = TEST_CMD_ADD,
        .c_name = "TEST_ADD",
        .c_maxattr = TEST_ATTR_MAX,
        .c_attr_policy = test_policy,
        .c_msg_parser = test_msg_add_parser,
    },
    {
        .c_id = TEST_CMD_DEL,
        .c_name = "TEST_DEL",
        .c_maxattr = TEST_ATTR_MAX,
        .c_attr_policy = test_policy,
        .c_msg_parser = test_msg_del_parser,
    },
};

static struct genl_ops test_geml_ops = {
    .o_name = "test",
    .o_hdrsize = 0, /* 用户私有报文头长度没有就设置0 */
    .o_cmds = test_genl_cmds,
    .o_ncmds = ARRAY_SIZE(test_genl_cmds),
};


int main(int argc, char **argv)
{
    if(argc != 3) {
        printf("%s ip port\n", argv[0]);
        return 0;
    }

    if(genl_register_family(&test_geml_ops) < 0) {
        printf("genl_register_family() failed\n");
        return -1;
    }

    int ret = 0;
    struct nl_sock *sock = nl_socket_alloc();
    if(NULL == sock) {
        printf("nl_socket_alloc() failed\n");
        return -1;
    }

    if(genl_connect(sock) < 0) {
        printf("genl_connect() failed.");
        ret = -1;
        goto out;
    }

    int genl_family_test_id = genl_ctrl_resolve(sock, "test");
    if(genl_family_test_id < 0) {
        printf("genl_ctrl_resolve() failed\n");
        ret = -1;
        goto out;
    }
    printf("genl_family_test_id: auto id=0x%X\n", genl_family_test_id);


    struct nl_msg *msg = nlmsg_alloc();
    if(NULL == msg) {
        printf("nlmsg_alloc() failed\n");
        ret = -1;
        goto out;
    }

    /*
     * 重点要搞清genlmsg_put()函数的传递参数
     */
    void *hdr = genlmsg_put(msg, getpid(), 1231, genl_family_test_id,
                            0, NLM_F_REQUEST, TEST_CMD_ADD, TEST_GENL_VERSION);
    if(NULL == hdr) {
        printf("genlmsg_put() failed\n");
        ret = -1;
        goto out;
    }

    in_addr_t ip = inet_addr(argv[1]);
    int err = nla_put_u32(msg, TEST_ATTR_IP, ip);
    if(err < 0) {
        printf("nla_put_u32() failed\n");
        ret = -1;
        goto out;
    }

    err = nla_put_u16(msg, TEST_ATTR_PORT, htons((uint16_t)(atoi(argv[2]))));
    if(err < 0) {
        printf("nla_put_u16() failed\n");
        ret = -1;
        goto out;
    }

    err = nl_send(sock, msg);
    nlmsg_free(msg);
    while(1) {
        if(nl_recvmsgs_default(sock) < 0) {
            printf("nl_recvmsgs_default() failed\n");
        }
    }

out:
    nl_close(sock);
    nl_socket_free(sock);


    return ret;
}


